#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <cstdlib>
#include <new>
#include <typeinfo>
#include <list>
#include <thread> // std::thread
#include <mutex>  // std::mutex

using namespace std;

// 参考: https://blog.csdn.net/misayaaaaa/article/details/53786215
// 参考: https://github.com/applenob/Cpp_Primer_Practice
// chapter 19

// chapter 12 动态内存

// 1.
// 在此代码的结尾，b1 和 b2 各包含多少个元素？
// StrBlob b1;
// {
// 	StrBlob b2 = {"a", "an", "the"};
// 	b1 = b2;
// 	b2.push_back("about");
// }

// 它们实际操作的是同一个vector，都包含4个元素。在代码的结尾，b2 被析构了，不影响 b1 的元素。
// 知识点15：使用动态内存的原因:让多个对象共享相同的底层数据。也就是说拷贝的情况虽然发生，但是并不是元素的拷贝，而是将本身的指向拷贝给另一个指针对象，让这一个对象也指向自己所指向的对象，
// 这样在本身释放以后，还有另一个对象指向自身原来所指向的对象。
// 由知识点15：b1和b2都包含4个元素


// 2.
// 编写你自己的StrBlob 类，包含const 版本的 front 和 back

class StrBlob {
public:
    using size_type = vector<string>::size_type;

    StrBlob():data(std::make_shared<vector<string>>()) { }
    StrBlob(std::initializer_list<string> il):data(std::make_shared<vector<string>>(il)) { }

    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }

    void push_back(const string &t) { data->push_back(t); }
    void pop_back() {
        check(0, "pop_back on empty StrBlob");
        data->pop_back();
    }

    std::string& front() {
        check(0, "front on empty StrBlob");
        return data->front();
    }

    std::string& back() {
        check(0, "back on empty StrBlob");
        return data->back();
    }

    const std::string& front() const {
        check(0, "front on empty StrBlob");
        return data->front();
    }
    const std::string& back() const {
        check(0, "back on empty StrBlob");
        return data->back();
    }

private:
    void check(size_type i, const string &msg) const {
        if (i >= data->size()) throw std::out_of_range(msg);
    }

private:
    std::shared_ptr<vector<string>> data;
};


// 3.
// StrBlob 需要const 版本的push_back 和 pop_back吗？如需要，添加进去。否则，解释为什么不需要
// 是否要定义为const版本取决于是否需要加以修改，这两个函数都不会对参数进行修改，所以无需加const



// 4.
// 在我们的 check 函数中，没有检查 i 是否大于0。为什么可以忽略这个检查？
// 因为 size_type 是一个无符号整型，当传递给 check 的参数小于 0 的时候，参数值会转换成一个正整数



// 5.
我们未编写接受一个 initializer_list explicit 参数的构造函数。讨论这个设计策略的优点和缺点。

解：

<<<<<<< HEAD:c++_practice_题目/src/c++_prime_questions_chapter_12.cpp
构造函数不是 explicit 的，意味着可以从 initializer_list 隐式转换为 StrBlob。在 StrBlob 对象中，只有一个数据成员 data，而 StrBlob 对象本身的含义，也是一个管理字符串的序列。
因此，从 initializer_list 到 StrBlob 的转换，在逻辑上是可行的。而这个设计策略的缺点，可能在某些地方我们确实需要 initializer_list，而编译器仍会将之转换为 StrBlob
=======
构造函数不是 explicit 的，意味着可以从 initializer_list 隐式转换为 StrBlob。在 StrBlob 对象中，只有一个数据成员 data，而 StrBlob 对象本身的含义，也是一个管理字符串的序列。因此，从 initializer_list 到 StrBlob 的转换，在逻辑上是可行的。而这个设计策略的缺点，可能在某些地方我们确实需要 initializer_list，而编译器仍会将之转换为 StrBlob
>>>>>>> 8ca32d5184cb852fee6cc29515709a7d218d67fc:c++_practice/src/c++_prime_questions_chapter_12.cpp
// explicit的作用就是抑制构造函数的隐式转换
// 优点：不会自动的进行类型转换，必须清楚的知道类类型
// 缺点：必须用构造函数显示创建一个对象，不够方便简单



// 6.
// 编写函数，返回一个动态分配的 int 的vector。将此vector 传递给另一个函数，这个函数读取标准输入，将读入的值保存在 vector 元素中。
// 再将vector传递给另一个函数，打印读入的值。记得在恰当的时刻delete vector
vector<int> *new_vector(void)
{
    return new vector<int>;
}

void read_ints(vector<int> *pv)
{
    int v;
    while (cin >> v)
    {
        pv->push_back(v);
    }
}

void print_ints(vector<int> *pv)
{
    for (const auto &v : *pv)
    {
        cout << v << " ";
        cout << endl;
    }
}


// 7.
// 智能指针的版本只是不需要在程序结束时进行delete操作
// 智能指针管理内存

shared_ptr<vector<int>> new_vector_1(void)
{
    return make_shared<vector<int>>();
}





// 8.
// 下面的函数是否有错误？如果有，解释错误原因
bool b() {
	int* p = new int;
	// ...
	return p;
}
// p是一个内置类型的指针，返回p会使得p的类型转化为bool类型，其指向的动态内存空间将无法得到释放


// 9.
// 解释下面代码执行的结果

// int *q = new int(42);
// int *r = new int(100);
// r = q;
// auto q2 = make_shared<int>(42);
// auto r2 = make_shared<int>(100);
// r2 = q2;
// q，r为内置类型指针，保存动态内存的地址，进行二次赋值之后，r原来指向的内存空间将得不到释放，造成内存泄漏
// q2，r2为智能指针，r2进行赋值之后，其计数器将减一，由于r2是指向该内存空间的唯一智能指针，所以该内存会得到释放



// 10.
// void process(shared_ptr<int> ptr){
// }

// 下面的代码调用了第413页中定义的process 函数，解释此调用是否正确。如果不正确，应如何修改？
// shared_ptr<int> p(new int(42));
// process(shared_ptr<int>(p));
// 正确。shared_ptr<int>(p) 会创建一个临时的智能指针，这个智能指针与 p 引用同一个对象，此时引用计数为 2。当表达式结束时，临时的智能指针被销毁，此时引用计数为 1



// 11.
// 如果我们像下面这样调用 process，会发生什么？
// process(shared_ptr<int>(p.get()));

// 永远不要用get初始化另一个智能指针或是给智能指针赋值
// 利用P的get()函数得到的内置指针来初始化一个临时的智能指针，一旦调用结束，该临时指针被销毁，内置指针所指对象内存会被释放，
// 使得p变为空悬指针，会使得同一个指针被释放两次




// 12.
// p 和 sp 的定义如下，对于接下来的对 process 的每个调用，如果合法，解释它做了什么，如果不合法，解释错误原因：

// auto p = new int();
// auto sp = make_shared<int>();
// (a) process(sp);
// (b) process(new int());
// (c) process(p);
// (d) process(shared_ptr<int>(p));
// 解：

// (a) 合法。将sp 拷贝给 process函数的形参，在函数里面引用计数为 2，函数结束后引用计数为 1。
// (b) 不合法。不能从内置指针隐式转换为智能指针。
// (c) 不合法。不能从内置指针隐式转换为智能指针。
// (d) 合法。但是智能指针和内置指针一起使用可能会出现问题，在表达式结束后智能指针会被销毁，它所指向的对象也被释放。而此时内置指针 p 依旧指向该内存空间。之后对内置指针 p 的操作可能会引发错误。




// 13.
// 如果执行下面的代码，会发生什么？
// auto sp = make_shared<int>();
// auto p = sp.get();
// delete p;

// 智能指针 sp 所指向空间已经被释放，再对 sp 进行操作会出现错误




// 14.



// 15.




// 16.
// unique_ptr拥有其所指向的对象，属于一一对应关系，unique_ptr被销毁时，其对象也会被销毁，
// unique_ptr不支持拷贝和赋值，必须采用直接初始化的方式，当我们定义一个unique_ptr时，
// 必须将其绑定到一个new返回的指针上

// 17.
// 下面的 unique_ptr 声明中，哪些是合法的，哪些可能导致后续的程序错误？解释每个错误的问题在哪里。

// int ix = 1024, *pi = &ix, *pi2 = new int(2048);
// typedef unique_ptr<int> IntP;
// (a) IntP p0(ix);
// (b) IntP p1(pi);
// (c) IntP p2(pi2);
// (d) IntP p3(&ix);
// (e) IntP p4(new int(2048));
// (f) IntP p5(p2.get());
// 解：

// (a) 不合法。在定义一个 unique_ptr 时，需要将其绑定到一个new 返回的指针上。
// (b) 不合法。理由同上。
// (c) 合法。但是也可能会使得 pi2 成为空悬指针。
// (d) 不合法。当 p3 被销毁时，它试图释放一个栈空间的对象。
// (e) 合法。
// (f) 不合法。p5 和 p2 指向同一个对象，当 p5 和 p2 被销毁时，会使得同一个指针被释放两次。



// 18.
// shared_ptr 为什么没有 release 成员？
// release 成员的作用是放弃控制权并返回指针，因为在某一时刻只能有一个 unique_ptr 指向某个对象，
// unique_ptr 不能被赋值，所以要使用 release 成员将一个 unique_ptr 的指针的所有权传递给另一个 unique_ptr。而 shared_ptr 允许有多个 shared_ptr 指向同一个对象，
// 因此不需要 release 成员
// release()函数的作用就是放弃对指针指向对象的控制权，但shared_ptr是多对一的关系，其他的智能指针仍然可以删除这个对象，所以这个函数的话对shared_ptr没意义




// 19.



// 20.





// 21.



// 22.
// 为了能让 StrBlobPtr 使用 const StrBlob，你觉得应该如何修改？
// 定义一个名为ConstStrBlobPtr 的类，使其能够指向 const StrBlob。

// 构造函数改为接受 const Strblob & , 然后给 Strblob 类添加两个 const 成员函数 cbegin 和 cend，返回 ConstStrBlobPtr



// 23.


// 24.



// 25.
// 给定下面的new表达式，你应该如何释放pa？
// int *pa = new int[10];
// delete [] pa;




// 26.


// 27.




// 28.


// 29.




// 30.



// 31.



// 32.




// 33.






int main()
{
    




    
int ix = 1024;
int* pi = &ix;
int* pi2 = new int(2048);

typedef unique_ptr<int> InitP;
// InitP p0(ix);
// InitP p1(pi);
InitP p2(pi2);
// InitP p3(&ix);   
// InitP p5(p2.get());


auto p =new int();
auto sp = make_shared<int>();

process(sp);
// process(new int());
// process(p);
process(shared_ptr<int>(p));



    // shared_ptr<int> p(new int(42));
    // process(shared_ptr<int>(p) );

    // process(shared_ptr<int>(p.get()));
    
    
    // vector<int>* pv = new_vector();
    // if(!pv){
    //     cout<<"内存不足"<<endl;
    // }

    // read_ints(pv);
    // print_ints(pv);
    // delete pv;
    // pv = nullptr;

    printf("hello c++ \n");
}